// const express = require("express");
// const app = express();
// const http = require('http').createServer(app);
// const io = require('socket.io')(http, { cors: true });

// app.use(express.json({ limit: '10mb' }));
// app.use(express.urlencoded({ extended: false, limit: '10mb' }));

// http.listen(6544, function () {
//     console.log(`地图服务已开启,${port.map}端口被监听...`);
// });


const { Server } = require("socket.io");

const io = new Server({ cors: true });

const { port, game } = require("./server/config/config.json");
const db_handler = require('./server/db_handler.js');
const Handler = require("./server/handler.js");
const { logger } = require("./server/logger.js");

const Player = require("./server/module/player.js");


logger.info("服务器启动...")

//全局玩家信息
global.players = new Map();
//全局玩家队伍信息
global.parties = new Map();
//云变量
const cloudVariables = {};
const cloudSwitches = {};
const cloudSelfSwitches = {};
//云开关自动刷新测试数据
const switchRefresh = {
    "3,3,A": { "value": false, "delay": 20000 }
}


io.on("connection", (socket) => {

    //登录
    socket.on("start", (identity, callback) => {
        const uid = Handler.getActorId(identity);
        db_handler.getActorInfo(uid, socket, (data) => {
            const { uid } = data;
            //只允许单线登录
            if (players.has(uid)) {
                socket.emit("tip pop", "该角色已在其他客户端登录，请先登出后再尝试登录...")
                return
            }
            //防止登录游戏过程中掉线保存导致地图id为0的情况
            if (data.map_id === 0) data.map_id = 3;
            //设置后端玩家对象
            data.socket = socket;
            const player = new Player(data);
            //登录
            player.logon();
            //标记玩家uid
            socket.playerUid = uid;
            //广播在线人数发生变化
            io.emit("player refresh", players.size);
        });
        callback({
            "state": "ok"
        })
    })

    //当断开连接
    socket.on('disconnect', () => {
        //如果uid不存在，通常发生在服务器报错重启后，强制客户端重新连接
        if (!socket.playerUid) return;
        const player = players.get(socket.playerUid);
        if (!player) return;
        //广播离开
        player.offline();
        //广播在线人数发生变化
        io.emit("player refresh", players.size);
    })

    //传送
    socket.on("transfer", (data, callback) => {
        //如果uid不存在，通常发生在服务器报错重启后，强制客户端重新连接
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const { newMapId } = data;
        const player = players.get(socket.playerUid);
        const room = MapServer.getRoomName(newMapId);
        player.transfer(data, room);
        //回调，用于客户端计算ping值，后略
        callback({
            "state": "ok"
        })
    })

    //移动
    socket.on("move", (data, callback) => {
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const player = players.get(socket.playerUid);
        const { x, y } = data;
        player.move(x, y);
        callback({
            "state": "ok"
        })
    })

    //客户端请求所在地图所有玩家数据事件
    socket.on('get map player', (mapId, callback) => {
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const room = MapServer.getRoomName(mapId);
        io.in(room).fetchSockets().then(sockets => {
            const playsData = [];
            for (const socket of sockets) {
                const player = players.get(socket.playerUid);
                const data = player.makeBaseData();
                playsData.push(data);
            }
            socket.emit("all players info", playsData);
        });
        callback({
            "state": "ok"
        })
    })

    //客户端请求玩家数据通过uid
    socket.on('get info of uid', (uid) => {
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const player = players.get(uid);
        const data = player.makeBaseData();
        socket.emit("players info", data);
    })

    //（队伍）客户端请求玩家数据通过uid
    socket.on('get pt-info of uid', (uid) => {
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const data = [];
        const player = players.get(socket.playerUid);
        const party = player.party;
        for (const member of uid) {
            const other = party.getData(member);
            data.push(other.makeBaseData());
        }
        socket.emit("pt-players info", data);
        return;
    })

    //改变玩家头顶显示当前状态
    socket.on("change map state", (data, callback) => {
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const player = players.get(socket.playerUid);
        player.changeMapState(data);
        callback({
            "state": "ok"
        })
    })

    //===================云变量==========================

    socket.on('switches change', (data) => {
        const { key, value } = data;
        cloudSwitches[key] = value;
        MapServer.switchRefresh(key);
        socket.broadcast.emit('switch change', data);
    })
    socket.on('variables change', (data) => {
        const { key, value } = data;
        cloudVariables[key] = value;
        socket.broadcast.emit('variable change', data);
    })
    socket.on('selfSwitches change', (data) => {
        const player = players.get(socket.playerUid);
        const { key, value } = data;
        cloudSelfSwitches[key] = value;
        MapServer.switchRefresh(key);
        logger.info(`独立开关 ${key} 被玩家 [${socket.playerUid}]${player.name} 触发...`)
        socket.broadcast.emit('selfSwitch change', data);
    })
    socket.on('get cloud variables', () => {
        const data = { cloudSwitches, cloudVariables, cloudSelfSwitches };
        socket.emit("cloud variables info", data);
    })

    //===================交互==========================

    //组队邀请
    socket.on("sending party invite", (uid) => {
        if (!socket.playerUid) {
            socket.emit("err link");
            return;
        }
        const player = players.get(socket.playerUid);
        const targetPlayer = players.get(uid);

        //获取队伍实例
        const party = player.getParty();
        if (parties.get(socket.playerUid) !== party) parties.set(socket.playerUid, party);

        //目标不在线情况
        if (!targetPlayer) {
            socket.emit("tip pop", "该玩家不在线！");
            return;
        }
        //目标已有队伍的情况
        if (targetPlayer.party) {
            socket.emit("tip pop", "该玩家已有队伍！");
            return
        }
        //目标有未处理邀请的情况
        if (targetPlayer.partyPend) {
            socket.emit("tip pop", "该玩家已有其他未处理的邀请！");
            return;
        }
        //邀请成功的情况
        targetPlayer.partyInvite(party);
        socket.emit("tip pop", "组队邀请已发送！");

    })

    //同意组队邀请
    socket.on("party agree", () => {
        const player = players.get(socket.playerUid);
        player.partyAgree();
        const party = player.party;
        const room = party.getRoom();
        const partyData = party.makeData();
        const code = 132;
        const obj = [player.name];
        const data = { partyData, code, obj };
        parties.set(socket.playerUid, party);
        io.in(room).emit("updata party", data);
    })

    //拒绝组队邀请
    socket.on("party refuse", () => {
        const player = players.get(socket.playerUid);
        player.partyRefuse();
    })

    //离开队伍
    socket.on("leave party", () => {
        const uid = socket.playerUid;
        const player = players.get(uid);
        player.leaveParty();
    })

    //聊天
    socket.on("chat", (msg) => {
        const type = 0;
        const message = [socket.player.name, msg];
        const data = { type, message }
        io.emit("chat A", data);
    })

    //===============战斗部分===============
    //战斗
    socket.on("battle", (battleData) => {
        const player = socket.player;
        const partyRoom = player.party.roomName;
        const battleMember = player.party.getBattleMember(battleData.member);

        const data = { partyRoom, battleMember, battleData }
        db_handler.getActorBattleInfo(data, MapServer.applyBattle);
    })
})

io.listen(6544, function () {
    console.log(`地图服务已开启,${port.map}端口被监听...`);
})

class MapServer {
    static getRoomName(mapId) {
        return `map-${mapId}`;
    }

    static switchRefresh(key) {
        const data = switchRefresh[key];
        if (data) {
            const { value, delay } = data;
            setTimeout(() => {
                cloudSelfSwitches[key] = value;
                const data = { key, value };
                io.emit('selfSwitch change', data);
                logger.info(`独立开关 ${key} 已刷新`);
            }, delay)
        }
    }
}

Array.prototype.remove = function (element) {
    for (; ;) {
        const index = this.indexOf(element);
        if (index >= 0) {
            this.splice(index, 1);
        } else {
            return this;
        }
    }
}